#include "mainwindow.h"

#include "dialogs/aboutdialog.h"
#include "editors/projectnavigator.h"
#include "perspectives/modelingperspective.h"
#include "perspectives/simulationperspective.h"
#include "perspectives/statisticsperspective.h"
#include "utils/modelvalidator.h"
#include "utils/undomanager.h"

#include <iconmanager.h>
#include <editorgallery.h>
#include <saveevent.h>
#include <sessionmanager.h>

#include <QtCore/QSettings>
#include <QtGui/QApplication>
#include <QtGui/QCloseEvent>
#include <QtGui/QDesktopWidget>
#include <QtGui/QDockWidget>
#include <QtGui/QMenuBar>
#include <QtGui/QToolBar>

using namespace core;
using namespace gui;
using namespace swp;

MainWindow* mainWindow = 0;

MainWindow* MainWindow::getInstance()
{
    if (mainWindow == 0)
    {
        mainWindow = new MainWindow();
        mainWindow->init();
    }

    return mainWindow;
}

void MainWindow::destroy()
{
    if (mainWindow != 0)
    {
        delete mainWindow;
        mainWindow = 0;
    }
}

QMenu* MainWindow::getMenu(const QString& menuId) const
{
    QMenu* ret = 0;

    if (m_menus.contains(menuId))
    {
        ret = m_menus[menuId];
    }

    return ret;
}

QToolBar* MainWindow::getToolBar(const QString& toolBarId) const
{
    QToolBar* ret = 0;

    if (m_toolBars.contains(toolBarId))
    {
        ret = m_toolBars[toolBarId];
    }

    return ret;
}

const IPerspective* MainWindow::getCurrentPerspective() const
{
    return m_perspectiveManager->getCurrent();
}

const QList<IPerspective* > MainWindow::getPerspectives() const
{
    return m_perspectiveManager->perspectives();
}

void MainWindow::changePerspective(int index)
{
    m_perspectiveManager->ensurePerspectiveRequest(index);
}

void MainWindow::registerForSaveNotification(const IPerspective* perspective,
    QObject* listener)
{
    if (!m_registeredListenersForSave.contains(perspective))
    {
        const QList<QObject* > list = (QList<QObject* >() << listener);
        m_registeredListenersForSave[perspective] = list;
    }
    else
    {
        m_registeredListenersForSave[perspective].append(listener);
    }
}

void MainWindow::unregisterForSaveNotification(const IPerspective* perspective,
    QObject* listener)
{
    if (m_registeredListenersForSave.contains(perspective))
    {
        m_registeredListenersForSave[perspective].removeAll(listener);

        if (m_registeredListenersForSave[perspective].size() == 0)
        {
            m_registeredListenersForSave.remove(perspective);
        }
    }
}

void MainWindow::registerForCloseNotification(const core::IPerspective* perspective,
    QObject* listener)
{
    if (!m_registeredListenersForClose.contains(perspective))
    {
        const QList<QObject* > list = (QList<QObject* >() << listener);
        m_registeredListenersForClose[perspective] = list;
    }
    else
    {
        m_registeredListenersForClose[perspective].append(listener);
    }
}

void MainWindow::unregisterForCloseNotification(const core::IPerspective* perspective,
    QObject* listener)
{
    if (m_registeredListenersForClose.contains(perspective))
    {
        m_registeredListenersForClose[perspective].removeAll(listener);

        if (m_registeredListenersForClose[perspective].size() == 0)
        {
            m_registeredListenersForClose.remove(perspective);
        }
    }
}

void MainWindow::putToBag(const QString& key, const QVariant& value)
{
    m_bag[key] = value;
}

QVariant MainWindow::bagValue(const QString& key) const
{
    QVariant ret;

    if (m_bag.contains(key))
    {
        ret = m_bag[key];
    }

    return ret;
}

void MainWindow::closeEvent(QCloseEvent* event)
{
    foreach (const QList<QObject* >& list,
        m_registeredListenersForClose.values())
    {
        foreach (QObject* listener, list)
        {
            QApplication::sendEvent(listener, event);

            if (!event->isAccepted())
            {
                break;
            }
        }
    }

    if (event->isAccepted())
    {
        writeSettings();
        writeSession();

        close();
    }
}

MainWindow::MainWindow() : QMainWindow(),
    m_bag(),
    m_registeredListenersForSave(),
    m_menus(),
    m_toolBars(),
    m_documentManager(DocumentManager::getInstance()),
    m_perspectiveManager(new PerspectiveManager(this)),
    m_perspectives(0),
    m_modelingPerspective(0),
    m_simulationPerspective(0),
    m_settingsDialog(0),
    m_projectMenu(0),
    m_editMenu(0),
    m_viewMenu(0),
    m_goMenu(0),
    m_toolsMenu(0),
    m_helpMenu(0),
    m_undoToolbar(0),
    m_undoAction(0),
    m_redoAction(0),
    m_undoMenuAction(0),
    m_redoMenuAction(0),
    m_validatorMenuAction(0),
    m_restoreSessionMenuAction(0),
    m_optionsMenuAction(0),
    m_goToModelingMenuAction(0),
    m_goToSimulationMenuAction(0),
    m_goToStatisticsMenuAction(0),
    m_aboutMenuAction(0)
{
    IconManager* iconManager = IconManager::getInstance();

    iconManager->registerStyleIcon("SwpUndo",
        ":/swp/icons/undo_icon.png");
    iconManager->registerStyleIcon("SwpRedo",
        ":/swp/icons/redo_icon.png");
    iconManager->registerStyleIcon("SwpUndoMenu",
        ":/swp/icons/undo_menu_icon.png");
    iconManager->registerStyleIcon("SwpRedoMenu",
        ":/swp/icons/redo_menu_icon.png");

    iconManager->registerStyleIcon("SwpModelingPerspective",
        ":/swp/icons/modeling_perspective_icon.png");
    iconManager->registerStyleIcon("SwpSimulationPerspective",
        ":/swp/icons/simulation_perspective_icon.png");
    iconManager->registerStyleIcon("SwpStatisticsPerspective",
        ":/swp/icons/statistics_perspective_icon.png");
    iconManager->registerStyleIcon("SwpModelingPerspectiveMenu",
        ":/swp/icons/modeling_perspective_menu_icon.png");
    iconManager->registerStyleIcon("SwpSimulationPerspectiveMenu",
        ":/swp/icons/simulation_perspective_menu_icon.png");
    iconManager->registerStyleIcon("SwpStatisticsPerspectiveMenu",
        ":/swp/icons/statistics_perspective_menu_icon.png");

    iconManager->registerStyleIcon("SwpValidatorMenu",
        ":/swp/icons/validator_menu_icon.png");
    iconManager->registerStyleIcon("SwpRestoreSessionMenu",
        ":/swp/icons/restore_session_menu_icon.png");
    iconManager->registerStyleIcon("SwpOptionsMenu",
        ":/swp/icons/options_menu_icon.png");

    const QDesktopWidget* desktop = qApp->desktop();
    const QRect screenSize = desktop->availableGeometry(
        desktop->primaryScreen());
    const int width = screenSize.width() - 150;
    const int height = screenSize.height() - 150;

    move((screenSize.width() - width) / 2,
        (screenSize.height() - height) / 2);
    resize(width, height);

    SessionManager::getInstance()->setSessionProvider(m_documentManager);
}

MainWindow::~MainWindow()
{

}

void MainWindow::init()
{
    readSettings();

    createActions();
    createMenus();
    addMenus();
    createPerspectives();
    createToolbars();
    addToolBars();

    connectSlots();

    setCentralWidget(m_perspectives);
    setWindowTitle(tr("Software process modeler"));

    readPerspectiveSettings();

    if (Settings::getInstance()->optionValue(
        "General", "RestoreSession").toBool())
    {
         SessionManager::getInstance()->restoreSession();
    }
}

void MainWindow::addMenus()
{
    m_menus[PROJECT_MENU] = m_projectMenu;
    m_menus[EDIT_MENU] = m_editMenu;
    m_menus[VIEW_MENU] = m_viewMenu;
    m_menus[GO_MENU] = m_goMenu;
    m_menus[TOOLS_MENU] = m_toolsMenu;
    m_menus[HELP_MENU] = m_helpMenu;
    m_menus[FINISHED] = 0;
}

void MainWindow::addToolBars()
{

}

void MainWindow::createActions()
{
    IconManager* iconManager = IconManager::getInstance();

    m_undoAction = new QAction(tr("&Undo"), this);
    if (iconManager->hasIcon("SwpUndo"))
    {
        m_undoAction->setIcon(iconManager->icon(
            "SwpUndo"));
    }

    m_redoAction = new QAction(tr("&Redo"), this);
    if (iconManager->hasIcon("SwpRedo"))
    {
        m_redoAction->setIcon(iconManager->icon(
            "SwpRedo"));
    }   

    m_undoMenuAction = new QAction(tr("&Undo"), this);
    m_undoMenuAction->setShortcuts(QKeySequence::Undo);
    m_undoMenuAction->setShortcutContext(Qt::WindowShortcut);
    if (iconManager->hasIcon("SwpUndoMenu"))
    {
        m_undoMenuAction->setIcon(iconManager->icon(
            "SwpUndoMenu"));
    }

    m_redoMenuAction = new QAction(tr("&Redo"), this);
    m_redoMenuAction->setShortcuts(QKeySequence::Redo);
    m_redoMenuAction->setShortcutContext(Qt::WindowShortcut);
    if (iconManager->hasIcon("SwpRedoMenu"))
    {
        m_redoMenuAction->setIcon(iconManager->icon(
            "SwpRedoMenu"));
    }

    m_validatorMenuAction = new QAction(tr("Run &validator"), this);
    m_validatorMenuAction->setShortcut(QKeySequence(
        Qt::CTRL + Qt::SHIFT + Qt::Key_V));
    m_validatorMenuAction->setShortcutContext(Qt::WindowShortcut);
    if (iconManager->hasIcon("SwpValidatorMenu"))
    {
        m_validatorMenuAction->setIcon(
            iconManager->icon("SwpValidatorMenu"));
    }

    m_restoreSessionMenuAction = new QAction(tr("&Restore session"), this);
    if (iconManager->hasIcon("SwpRestoreSessionMenu"))
    {
        m_restoreSessionMenuAction->setIcon(
            iconManager->icon("SwpRestoreSessionMenu"));
    }

    m_optionsMenuAction = new QAction(tr("&Options"), this);
    if (iconManager->hasIcon("SwpOptionsMenu"))
    {
        m_optionsMenuAction->setIcon(iconManager->icon("SwpOptionsMenu"));
    }

    m_goToModelingMenuAction = new QAction(tr("&Modeling"), this);
    if (iconManager->hasIcon("SwpModelingPerspectiveMenu"))
    {
        m_goToModelingMenuAction->setIcon(iconManager->icon(
            "SwpModelingPerspectiveMenu"));
    }
    m_goToSimulationMenuAction = new QAction(tr("S&imulation"), this);
    if (iconManager->hasIcon("SwpSimulationPerspectiveMenu"))
    {
        m_goToSimulationMenuAction->setIcon(iconManager->icon(
            "SwpSimulationPerspectiveMenu"));
    }
    m_goToStatisticsMenuAction = new QAction(tr("S&tatistics"), this);
    if (iconManager->hasIcon("SwpStatisticsPerspectiveMenu"))
    {
        m_goToStatisticsMenuAction->setIcon(iconManager->icon(
            "SwpStatisticsPerspectiveMenu"));
    }

    m_aboutMenuAction = new QAction(tr("&About"), this);

    UndoManager::getInstance()->bindActions(m_undoAction, m_redoAction,
        m_undoMenuAction, m_redoMenuAction);
}

void MainWindow::createMenus()
{
    m_projectMenu = menuBar()->addMenu(tr("&PROJECT"));

    m_editMenu = menuBar()->addMenu(tr("&EDIT"));
    m_editMenu->addAction(m_undoMenuAction);
    m_editMenu->addAction(m_redoMenuAction);

    m_viewMenu = menuBar()->addMenu(tr("&VIEW"));

    m_goMenu = menuBar()->addMenu(tr("&GO"));
    m_goMenu->addAction(m_goToModelingMenuAction);
    m_goMenu->addAction(m_goToSimulationMenuAction);
    m_goMenu->addAction(m_goToStatisticsMenuAction);

    m_toolsMenu = menuBar()->addMenu(tr("&TOOLS"));
    m_toolsMenu->addAction(m_validatorMenuAction);
    m_toolsMenu->addAction(m_restoreSessionMenuAction);
    m_toolsMenu->addAction(m_optionsMenuAction);

    m_helpMenu = menuBar()->addMenu(tr("&HELP"));
    m_helpMenu->addAction(m_aboutMenuAction);
}

void MainWindow::createToolbars()
{
    m_undoToolbar = addToolBar(tr("Undo"));
    m_undoToolbar->setObjectName("UndoToolbar");
    m_undoToolbar->addAction(m_undoAction);
    m_undoToolbar->addAction(m_redoAction);
}

void MainWindow::createStatusBar()
{
    // do nothing
}

void MainWindow::createPerspectives()
{
    IconManager* iconManager = IconManager::getInstance();

    m_perspectives = new QTabWidget(this);
    m_perspectives->setTabPosition(QTabWidget::East);

    m_modelingPerspective = new ModelingPerspective(m_perspectiveManager,
        m_perspectives);
    m_simulationPerspective = new SimulationPerspective(m_perspectiveManager,
        m_perspectives);
    m_statisticsPerspective = new StatisticsPerspective(m_perspectiveManager,
        m_perspectives);

    m_perspectiveManager->addPerspective(m_modelingPerspective);
    m_perspectiveManager->addPerspective(m_simulationPerspective);
    m_perspectiveManager->addPerspective(m_statisticsPerspective);

    if (iconManager->hasIcon("SwpModelingPerspective"))
    {
        m_perspectives->addTab(m_modelingPerspective,
            iconManager->icon("SwpModelingPerspective"), tr("Modeling"));
    }
    else
    {
        m_perspectives->addTab(m_modelingPerspective, tr("Modeling"));
    }

    if (iconManager->hasIcon("SwpSimulationPerspective"))
    {
        m_perspectives->addTab(m_simulationPerspective,
            iconManager->icon("SwpSimulationPerspective"), tr("Simulation"));
    }
    else
    {
        m_perspectives->addTab(m_simulationPerspective, tr("Simulation"));
    }

    if (iconManager->hasIcon("SwpStatisticsPerspective"))
    {
        m_perspectives->addTab(m_statisticsPerspective,
            iconManager->icon("SwpStatisticsPerspective"), tr("Statistics"));
    }
    else
    {
        m_perspectives->addTab(m_statisticsPerspective, tr("Statistics"));
    }

    m_perspectiveManager->setCurrent(m_modelingPerspective);
    m_perspectives->setCurrentIndex(0);
}

void MainWindow::connectSlots()
{    
    connect(m_goToModelingMenuAction, SIGNAL(triggered()),
        this, SLOT(goToModeling()));
    connect(m_goToSimulationMenuAction, SIGNAL(triggered()),
        this, SLOT(goToSimulation()));
    connect(m_goToStatisticsMenuAction, SIGNAL(triggered()),
        this, SLOT(goToStatistics()));

    connect(m_undoAction, SIGNAL(triggered()),
        UndoManager::getInstance(), SLOT(undo()));
    connect(m_redoAction, SIGNAL(triggered()),
        UndoManager::getInstance(), SLOT(redo()));

    connect(m_undoMenuAction, SIGNAL(triggered()),
        UndoManager::getInstance(), SLOT(undo()));
    connect(m_redoMenuAction, SIGNAL(triggered()),
        UndoManager::getInstance(), SLOT(redo()));

    connect(m_validatorMenuAction, SIGNAL(triggered()),
        this, SLOT(runValidation()));
    connect(m_restoreSessionMenuAction, SIGNAL(triggered()),
        SessionManager::getInstance(), SLOT(restoreSession()));
    connect(m_optionsMenuAction, SIGNAL(triggered()),
        this, SLOT(openOptions()));

    connect(m_perspectives, SIGNAL(currentChanged(int)),
        m_perspectiveManager, SLOT(perspectiveChange(int)));

    connect(m_perspectiveManager,
        SIGNAL(perspectiveChanged(core::IPerspective*)),
        UndoManager::getInstance(),
        SLOT(handlePerspectiveChange(core::IPerspective*)));

    connect(m_perspectiveManager, SIGNAL(ensurePerspectiveRequested(int)),
        m_perspectives, SLOT(setCurrentIndex(int)));

    connect(UndoManager::getInstance(), SIGNAL(currentStackCleared(
        QSharedPointer<core::IDocument>)),
            m_documentManager, SLOT(checkCleanState(
                QSharedPointer<core::IDocument>)));
    connect(m_documentManager, SIGNAL(documentSaved(
        QSharedPointer<core::IDocument>)),
            UndoManager::getInstance(), SLOT(handleDocumentSave(
                QSharedPointer<core::IDocument>)));

    connect(m_aboutMenuAction, SIGNAL(triggered()),
        this, SLOT(showAbout()));

    m_perspectiveManager->setCurrent(m_modelingPerspective); // ensure Undo
}

void MainWindow::readSettings()
{
    QSettings appSettings(QSettings::NativeFormat, QSettings::UserScope,
        "VSB", "SWPM");

    Settings* settings = Settings::getInstance();
    settings->read(&appSettings);
}

void MainWindow::readPerspectiveSettings()
{
    QSettings appSettings(QSettings::NativeFormat, QSettings::UserScope,
        "VSB", "SWPM");

    const QStringList groups = appSettings.childGroups();

    if (groups.contains("MainWindow"))
    {
        appSettings.beginGroup("MainWindow");
        move(appSettings.value("Pos").toPoint());
        resize(appSettings.value("Size").toSize());
        appSettings.endGroup();
    }

    appSettings.beginGroup("MainWindowState");
    restoreState(appSettings.value("state").toByteArray());
    appSettings.endGroup();

    // settings for perspectives
    m_modelingPerspective->readSettings(&appSettings);
    m_simulationPerspective->readSettings(&appSettings);
    m_statisticsPerspective->readSettings(&appSettings);
}

void MainWindow::writeSettings()
{
    QSettings appSettings(QSettings::NativeFormat, QSettings::UserScope,
        "VSB", "SWPM");

    if (!isMinimized() && !isMaximized())
    {
        appSettings.beginGroup("MainWindow");
        appSettings.setValue("Pos", pos());
        appSettings.setValue("Size", size());
        appSettings.endGroup();
    }

    appSettings.beginGroup("MainWindowState");
    appSettings.setValue("state", saveState());
    appSettings.endGroup();

    // settings for perspectives
    m_modelingPerspective->writeSettings(&appSettings);
    m_simulationPerspective->writeSettings(&appSettings);
    m_statisticsPerspective->writeSettings(&appSettings);

    Settings* settings = Settings::getInstance();
    settings->write(&appSettings);
}

void MainWindow::writeSession()
{
    SessionManager::getInstance()->persistSession();
}

// private slots
void MainWindow::openOptions()
{
    if (m_settingsDialog == 0)
    {
        m_settingsDialog = new SettingsDialog(Settings::getInstance(),
            true, this);
    }

    m_settingsDialog->exec();
}

void MainWindow::runValidation()
{
    m_perspectives->setCurrentIndex(0);

    ModelValidator::getInstance()->validate(
        m_modelingPerspective->projectNavigator()->selectedProject());
}

void MainWindow::goToModeling()
{
    m_perspectives->setCurrentIndex(0);
}

void MainWindow::goToSimulation()
{
    m_perspectives->setCurrentIndex(1);
}

void MainWindow::goToStatistics()
{
    m_perspectives->setCurrentIndex(2);
}

void MainWindow::sendSaveEvent()
{
    SaveEvent* saveEvent = new SaveEvent();

    const IPerspective* current = getCurrentPerspective();

    if (m_registeredListenersForSave.contains(current))
    {
        foreach (QObject* listener, m_registeredListenersForSave[current])
        {
            if (!saveEvent->isAccepted())
            {
                QApplication::sendEvent(listener, saveEvent);
            }
            else
            {
                break;
            }
        }
    }

    delete saveEvent;
}

void MainWindow::showAbout()
{
    AboutDialog dialog(this);

    dialog.exec();
}

void MainWindow::handleEditorChange(
    const QSharedPointer<IEditor>& editor)
{
    // find perspective
    IPerspective* perspective = 0;

    QSharedPointer<IDocument> doc;

    if (!editor.isNull())
    {
        foreach (IPerspective* _per, m_perspectiveManager->perspectives())
        {
            const QList<QSharedPointer<IEditor> > editors =
                _per->getEditorArea()->openedEditors(editor->document());

            if (editors.contains(editor))
            {
                perspective = _per;
                break;
            }
        }

        Q_ASSERT(perspective != 0); // can't happen

        doc = editor->document();
    }

    UndoManager::getInstance()->handleChangeOfDocument(perspective,
        editor.isNull() ? QSharedPointer<IDocument>(0) : doc);
}
